package org.raymond.iworks.study.performance.disk;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Stream;

public class IOTest {
    public static void main(String[] args) throws IOException {
        IOTest ioTest = new IOTest();
        String file0 = "/Users/raymond/1GB.data";
        String file = "tmp/1MB.data";
        // ioTest.biois(file);
        // ioTest.bioisreader(file);
        ioTest.bioreader(file0); // 6.4s for 1GB, 52ms for 1MB
        // ioTest.stream(file);
        // ioTest.scanner(file);
        // bioreader>biois>bioisreader>stream>scanner for 1MB file

        // nio
        // ioTest.readAllLines(file); // 9.7s for 1GB, 55ms for 1MB
        // ioTest.newBufferedReader(file); // 9.3s for 1GB
        // newBufferedReader>readAllLines>>bioreader

        ioTest.directBuffer(file0);
        ioTest.mapped(file0);
    }

    public void biois(String filepath) throws IOException {
        File file = new File(filepath);
        FileInputStream in = new FileInputStream(file);
        byte[] buffer = new byte[1024];
        long start = System.nanoTime();
        while (in.read(buffer) > -1) {
            String line = new String(buffer);
        }
        long end = System.nanoTime();
        System.out.println("biois: " + (end - start));
    }

    public void bioisreader(String filepath) {
        File file = new File(filepath);
        try (FileInputStream fis = new FileInputStream(file);
             BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
            long start = System.nanoTime();
            String line;
            while ((line = reader.readLine()) != null) {
                // 处理每一行数据
            }
            long end = System.nanoTime();
            System.out.println("bioisreader: " + (end - start));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void bioreader(String filepath) {
        File file = new File(filepath);
        try (FileReader fr = new FileReader(file);
             BufferedReader reader = new BufferedReader(fr)) {
            long start = System.nanoTime();
            String line;
            while ((line = reader.readLine()) != null) {
                // 处理每一行数据
            }
            long end = System.nanoTime();
            System.out.println("bioreader: " + (end - start)/1024/1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stream(String filepath) {
        Path path = Paths.get(filepath);
        long start = System.nanoTime();
        try (Stream<String> lines = Files.lines(path)) {
            lines.parallel().forEach(line -> {
                // 处理每一行数据
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("stream: " + (end - start));
    }

    public void scanner(String filepath) {
        File file = new File(filepath);
        try (Scanner scanner = new Scanner(file)) {
            long start = System.nanoTime();
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                // 处理每一行数据

                // 或者逐个单词读取
                //while (scanner.hasNext()) {
                //    String word = scanner.next();
                //    // 处理每个单词
                //}
            }
            long end = System.nanoTime();
            System.out.println("scanner: " + (end - start));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void readAllLines(String filepath) {
        List<String> content = new ArrayList<>();
        try {
            long start = System.nanoTime();
            content = Files.readAllLines(Paths.get(filepath), StandardCharsets.UTF_8);
            long end = System.nanoTime();
            System.out.println("readAllLines: " + (end - start));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public void newBufferedReader(String filepath) {
        List<String> content = new ArrayList<>();
        long start = System.nanoTime();
        try (BufferedReader br = Files.newBufferedReader(Paths.get(filepath), StandardCharsets.UTF_8)) {
            String line;
            while ((line = br.readLine()) != null) {
                content.add(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("newBufferedReader: " + (end - start)/1000/1000);
    }

    public void directBuffer(String filepath) throws IOException {
        FileChannel fileChannel = FileChannel.open(Paths.get(filepath));
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
        long start = System.nanoTime();
        while (true) {
            int len = fileChannel.read(byteBuffer);
            if (len == -1) {
                break;
            }
            byteBuffer.flip();
            byteBuffer.clear();
        }
        long end = System.nanoTime();
        System.out.println("directBuffer: " + (end - start)/1000/1000);
    }

    public void mapped(String filepath) throws IOException {
        FileChannel fileChannel = FileChannel.open(Paths.get(filepath));
        MappedByteBuffer mappedByteBuffer = null;
        long start = System.nanoTime();
        long pos = 0;
        long remain = fileChannel.size();
        long size = 1024;
        while (remain>0) {
            if(remain<size){
                size = remain;
            }
            mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, size);
            pos += size;
            remain -= size;
        }
        long end = System.nanoTime();
        System.out.println("mapped: " + (end - start)/1000/1000);
    }
}
